/*
 * interprete.c
 *
 *  Created on: 10/10/2011
 *      Author: utn_so
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#define SOCK_PATH     "/home/utn_so/Desarrollo/Workspace/mysocket"
#define SECTORES_PISTA 10
#define CANT_CABEZAS 2
#define RPM 6000
#define TTRANS 1
#define TLECTURA 1
#define TESCRITURA 1
#define TPISTAS 1

typedef struct CHS {
	int cilindro;
	int cabeza;
	int sector;
} t_direccion_fisica;

typedef struct Nodo {
	int elemento;
	struct Nodo *siguiente;
} nodo;

typedef nodo* cola;

int posActualCabezal = 0;
cola var_cola;

int abrirArchivo(char *filepath, struct stat *datos);
char *leerSector(int n_sector, struct stat estado, int archivo);
void escribirSector(int n_sector, int archivo, struct stat estado, char* datos,
		int longitud_datos);
void interpretar(int socket, char* buffer, char* funcion, cola cola);
void clean(int socket, char*buffer, int posInicial, int posFinal);
void info();
void trace(int socket, char* buffer, int *parametros, cola cola);
int tiempo_pista(int inicio, int fin);
t_direccion_fisica obtener_dir_fis(int dir_logica);
void INSERTAR_PRIMERO(int x, cola C);
void PONER_EN_COLA(int x, cola C);
cola QUITAR_DE_COLA(int* sector, cola C);
cola CREAR();

t_direccion_fisica obtener_dir_fis(int dir_logica) {
	t_direccion_fisica direccion;

	direccion.cilindro = dir_logica / (SECTORES_PISTA * CANT_CABEZAS);

	direccion.cabeza = (dir_logica % (SECTORES_PISTA * CANT_CABEZAS))
			/ (SECTORES_PISTA);

	direccion.sector = ((dir_logica % (SECTORES_PISTA * CANT_CABEZAS))
			% (SECTORES_PISTA));

	return direccion;
}

cola CREAR() {
	cola C;
	C = (nodo *) malloc(sizeof(nodo));
	C->siguiente = NULL;
	C->elemento = 0;
	return C;
}

void INSERTAR_PRIMERO(int x, cola C)

{
	C->elemento = x;
}

void PONER_EN_COLA(int x, cola C)

{
	nodo *actual, *ptr;
	actual = C;
	if ((actual->siguiente) != NULL) {
		while (abs(actual->elemento - x)
				> abs(actual->siguiente->elemento - actual->elemento)) {
			actual = actual->siguiente;
			if ((actual->siguiente) == NULL
			)
				break;
		}
		if ((actual->siguiente) != NULL) {
			if (abs(actual->elemento - x)
					== abs(actual->siguiente->elemento - actual->elemento)) {
				actual = actual->siguiente; // Si la cercania es igual decide por FIFO
			}
		}
	}
	ptr = (nodo *) malloc(sizeof(nodo));
	ptr->elemento = x;
	ptr->siguiente = actual->siguiente;
	actual->siguiente = ptr;
}

cola QUITAR_DE_COLA(int* sector, cola C) {
	cola aux;
	*sector = 0;
	if (C != NULL)
	{
		aux = C;
		*sector = aux->elemento;
		C = C->siguiente;
		free(aux);
		aux = NULL;
	};
	return C;
}

int tiempo_pista(int inicio, int fin) {
	t_direccion_fisica dir_inicial, dir_final;
	int ST, RT, TTotal, tiempoPosSector;
	dir_inicial = obtener_dir_fis(inicio);
	dir_final = obtener_dir_fis(fin);
	tiempoPosSector = (60000 / RPM) / SECTORES_PISTA;
	ST = abs(dir_inicial.cilindro - dir_final.cilindro) * TPISTAS;
	dir_inicial.sector += 1;
	if (dir_inicial.sector >= dir_final.sector) {
		dir_inicial.sector = (SECTORES_PISTA - dir_inicial.sector
				+ dir_final.sector);
	}
	RT = abs(dir_inicial.sector - dir_final.sector) * tiempoPosSector;
	TTotal = ST + RT + TTRANS;
	return (TTotal);
}

int abrirArchivo(char *filepath, struct stat *datos) {
	int sizeofpage, archivo;
	archivo = open(filepath, O_RDWR);
	if (archivo < 0) {
		perror("No se puede abrir el archivo");
	};
	sizeofpage = getpagesize();
	posix_madvise(NULL, sizeofpage, MADV_SEQUENTIAL);
	stat(filepath, datos);
	return archivo;
}

char *leerSector(int n_sector, struct stat estado, int archivo) {
	char *data;
	static char buffer[512];
	int NroPagina, sizeofpage, offset;
	sizeofpage = getpagesize();
	NroPagina = (n_sector / 8) * sizeofpage; // Cociente de la division //
	offset = (n_sector % 8) * 512; // Resto de la division //
	data = (char *) mmap(NULL, estado.st_size, PROT_READ, MAP_SHARED, archivo,
			NroPagina);
	data += offset;
	memcpy(buffer, data, 512);
	munmap(data, sizeofpage);
	posActualCabezal = n_sector + 1;
	return buffer;
}

void escribirSector(int n_sector, int archivo, struct stat estado, char* datos,
		int longitud_datos) {
	char *data;
	int NroPagina, sizeofpage, offset;
	sizeofpage = getpagesize();
	NroPagina = (n_sector / 8) * sizeofpage; // Cociente de la division //
	offset = (n_sector % 8) * 512; // Resto de la division //
	data = (char *) mmap(NULL, estado.st_size, PROT_READ | PROT_WRITE
	, MAP_SHARED, archivo, NroPagina);
	memcpy(data + offset, datos, longitud_datos);
	msync(data, sizeofpage, MS_SYNC);
	munmap(data, sizeofpage);
	posActualCabezal = n_sector + 1;
}

void interpretar(int socket, char* buffer, char* funcion, cola cola) {

	char *nombreFuncion, *siguienteToken, *actual;
	int parametros[5] = { -1, -1, -1, -1, -1 }, i = 0;

	nombreFuncion = strtok_r(funcion, " ", &siguienteToken);

	switch (nombreFuncion[0]) {
	case ('i'):
		info(socket, buffer);
		break;

	case ('c'):
		parametros[0] = atoi(strtok_r(siguienteToken, " ", &siguienteToken));
		parametros[1] = atoi(strtok_r(siguienteToken, " ", &siguienteToken));
		clean(socket, buffer, parametros[0], parametros[1]);
		break;
	case ('t'):
		while (nombreFuncion != NULL) {
			actual = siguienteToken;
			nombreFuncion = strtok_r(actual, " ", &siguienteToken);
			if (nombreFuncion != NULL)
			{
				parametros[i] = atoi(nombreFuncion);
			};
			i++;
		}
		;
		trace(socket, buffer, parametros, var_cola);
		break;
	default:
		sprintf(buffer, "La funcion %s no es soportada \n", nombreFuncion);
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		}
		break;
	}
}

void info(int socket, char *buffer) {
	sprintf(buffer, "La posicion actual del cabezal es %d\n", posActualCabezal);
	if (send(socket, buffer, strlen(buffer), 0) == -1) {
		perror("send");
		exit(1);}
	sprintf(buffer, "----------------- Fin Info ----------------- \n");
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		}
	}


void clean(int socket, char* buffer, int posInicial, int posFinal) {
	int posActual, archivo;
	char *filepath = "/home/utn_so/fat32.disk";
	char *blancos;
	struct stat estado;
	struct stat *puntEstado;
	puntEstado = &estado;
	archivo = abrirArchivo(filepath, puntEstado);
	blancos = (char*) malloc(512);
	memset(blancos, '\0', 512);
	for (posActual = posInicial; posActual <= posFinal; posActual++) {
		escribirSector(posActual, archivo, estado, blancos, 512);
	}
	sprintf(buffer, "----------------- Fin Clean ----------------- \n");
			if (send(socket, buffer, strlen(buffer), 0) == -1) {
				perror("send");
				exit(1);
			}
}

void trace(int socket, char* buffer, int *parametros, cola var_cola) {
	int i = 0, tiempoTotal = 0, *sectorSacado = NULL;
	sectorSacado = (int*) malloc(sizeof(int));
	t_direccion_fisica dir_fis_sect_solicitado, dir_fis_sect_actual,
			dir_fisica_proximo;
	if (parametros[i] != -1)
		INSERTAR_PRIMERO(parametros[i], var_cola);
	i++;
	while (parametros[i] != -1 && i <= 5) {
		PONER_EN_COLA(parametros[i], var_cola);
		i++;
	}
	i--;
	while (*sectorSacado != parametros[i]) {
		dir_fis_sect_actual = obtener_dir_fis(posActualCabezal);
		sprintf(buffer, "Posicion actual: %d:%d \n",
				dir_fis_sect_actual.cilindro, dir_fis_sect_actual.sector);
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		} // Esto envia por socket la posicion actual
		var_cola = QUITAR_DE_COLA(sectorSacado, var_cola);
		dir_fis_sect_solicitado = obtener_dir_fis(*sectorSacado);
		sprintf(buffer, "Sector solicitado: %d:%d \n",
				dir_fis_sect_solicitado.cilindro,
				dir_fis_sect_solicitado.sector);
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		}
		sprintf(buffer, "Sectores recorridos: ");
		if (posActualCabezal < *sectorSacado) {
			t_direccion_fisica actual;
			while (posActualCabezal < *sectorSacado) {
				actual = obtener_dir_fis(posActualCabezal + 1);
				sprintf(buffer, "%s %d:%d, ", buffer, actual.cilindro,
						actual.sector);
				posActualCabezal++;
			}

		}
		if (posActualCabezal > *sectorSacado) {
			t_direccion_fisica actual;
			while (posActualCabezal > *sectorSacado) {
				actual = obtener_dir_fis(posActualCabezal - 1);
				sprintf(buffer, "%s %d:%d, ", buffer, actual.cilindro,
						actual.sector);
				posActualCabezal--;
			}
		}
		sprintf(buffer, "%s \n", buffer);
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		}
		tiempoTotal = tiempo_pista(posActualCabezal, *sectorSacado);
		sprintf(buffer, "Tiempo consumido: %dms\n", tiempoTotal);
		if (send(socket, buffer, strlen(buffer), 0) == -1) {
			perror("send");
			exit(1);
		}
		posActualCabezal = *sectorSacado + 1;

		if (var_cola != NULL) {
			dir_fisica_proximo = obtener_dir_fis(var_cola->elemento);
			sprintf(buffer, "Proximo sector: %d:%d \n",
					dir_fisica_proximo.cilindro, dir_fisica_proximo.sector);
			if (send(socket, buffer, strlen(buffer), 0) == -1) {
				perror("send");
				exit(1);
			}
		} else {
			sprintf(buffer, "----------------- Fin Trace ----------------- \n");
			if (send(socket, buffer, strlen(buffer), 0) == -1) {
				perror("send");
				exit(1);

			}
		}
	}
}

int main(int argc, char **argv) {
	int s, s2, t, len, n;
	var_cola = CREAR();
	struct sockaddr_un local, remote;
	char str[100];
	pid_t child_pid;
	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}

	local.sun_family = AF_UNIX;
	strcpy(local.sun_path, SOCK_PATH);
	unlink(local.sun_path);
	len = strlen(local.sun_path) + sizeof(local.sun_family);
	if (bind(s, (struct sockaddr *) &local, len) == -1) {
		perror("bind");
		exit(1);
	}

	if (listen(s, 1) == -1) {
		perror("listen");
		exit(1);
	}
	n = 0;
	printf("Waiting for a connection...\n");
	child_pid = fork();
	  if(child_pid == 0) {
	    /* This is done by the child process. */

	    execv("/home/utn_so/Desarrollo/Workspace/ConsolaPPD/Debug/ConsolaPPD", argv);

	    /* If execv returns, it must have failed. */

	    printf("Unknown command\n");
	    exit(0);
	  }
	t = sizeof(remote);
	if ((s2 = accept(s, (struct sockaddr *) &remote, &t)) == -1) {
		perror("accept");
		exit(1);
	}

	printf("Connected.\n");

	while (n >= 0) {
		memset(&str[0],'\0',sizeof(char)*strlen(str));
		n = recv(s2, str, 100, 0);
		if (str[0] == 'e')
			break;
		if (n <= 0) {
			if (n < 0)
				perror("recv");
		}
		if (str[0] == 'e')
			exit(0);

		interpretar(s2, str, str, var_cola);
	}
	close(s2);
	return (0);
}
